package com.bytedance.douyinclouddemo.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.bytedance.douyinclouddemo.entity.OrderInfoDTO;
import com.bytedance.douyinclouddemo.model.JsonResponse;
import com.bytedance.douyinclouddemo.model.LiveDataModel;
import com.bytedance.douyinclouddemo.model.LivePlayAPIResponse;
import com.bytedance.douyinclouddemo.utils.DyLiveRoom;
import com.bytedance.douyinclouddemo.utils.RedisUtil;
import com.bytedance.douyinclouddemo.utils.UNicode2Str;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.tomcat.util.buf.CharsetUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 抖音云x弹幕玩法的服务端demo展示
 */
@RestController
@Slf4j
public class LivePlayDemoController {
    @Autowired
    private RedisUtil redisUtil;
    @GetMapping("/testRedis")
    public JsonResponse testRedis() {
        redisUtil.set("test", "test-value");
        String test = (String) redisUtil.get("test");
        JsonResponse response = new JsonResponse();
        response.success(test);
        return response;
    }

    @GetMapping("/getLiveRoom")
    public JsonResponse getLiveRoom(String token) {
        JSONObject liveRoomInfo = DyLiveRoom.getLiveRoomInfo(token);
        //redisUtil.set("test", "test-value");
        JsonResponse response = new JsonResponse();
        response.success(liveRoomInfo);
        log.info("getLiveRoom response : {}", JSONObject.toJSONString(response));
        return response;
    }

    /**
     * 开始玩法对局，玩法开始前调用
     */
    @PostMapping(path = "/start_game")
    public JsonResponse callContainerExample(HttpServletRequest httpRequest) {
        // 开发者可以直接通过请求头获取直播间信息,无需自行通过token置换

        // 应用id
        String appID = httpRequest.getHeader("X-TT-AppID");
        // 直播间id
        String roomID = httpRequest.getHeader("X-Room-ID");
        // 主播id
        String anchorOpenID = httpRequest.getHeader("X-Anchor-OpenID");
        // 主播头像url
        String avatarUrl = httpRequest.getHeader("X-Avatar-Url");
        // 主播昵称
        String nickName = httpRequest.getHeader("X-Nick-Name");

        log.info("appID: {}, roomID: {}, anchorOpenID: {}, avatarUrl: {}, nickName: {}", appID,
                roomID, anchorOpenID, avatarUrl, nickName);


        // 调用弹幕玩法服务端API，开启直播间推送任务，开启后，开发者服务器会通过/live_data_callback接口 收到直播间玩法指令
        List<String> msgTypeList = new ArrayList<>();
        msgTypeList.add("live_like");
        msgTypeList.add("live_comment");
        msgTypeList.add("live_gift");
        msgTypeList.add("live_fansclub");

        for (String msgType : msgTypeList) {
            boolean result = startLiveDataTask(appID, roomID, msgType);
            if (result) {
                log.info("{} 推送开启成功", msgType);
            } else {
                log.error("{} 推送开启失败", msgType);
            }
        }

        JsonResponse response = new JsonResponse();
        response.success("开始玩法对局成功");
        return response;
    }

    /**
     * startLiveDataTask: 开启推送任务：<a href="https://developer.open-douyin.com/docs/resource/zh-CN/interaction/develop/server/live/danmu#%E5%90%AF%E5%8A%A8%E4%BB%BB%E5%8A%A1">...</a>
     *
     * @param appID   小玩法appID
     * @param roomID  直播间ID
     * @param msgType 评论/点赞/礼物/粉丝团
     */
    private boolean startLiveDataTask(String appID, String roomID, String msgType) {
        // example: 通过java OkHttp库发起http请求,开发者可使用其余http访问形式
        OkHttpClient client = new OkHttpClient();
        String body = new JSONObject()
                .fluentPut("roomid", roomID)
                .fluentPut("appid", appID)
                .fluentPut("msg_type", msgType)
                .toString();
        Request request = new Request.Builder()
                .url("http://webcast.bytedance.com/api/live_data/task/start") // 内网专线访问小玩法openAPI,无需https协议
                .addHeader("Content-Type", "application/json") // 无需维护access_token
                .post(
                        okhttp3.RequestBody.create(
                                MediaType.get("application/json; charset=utf-8"),
                                body
                        )
                )
                .build();

        try {
            Response httpResponse = client.newCall(request).execute();
            if (httpResponse.code() != 200) {
                log.error("开启推送任务失败,http访问非200");
                return false;
            }
            LivePlayAPIResponse livePlayAPIResponse
                    = JSON.parseObject(httpResponse.body().string(), LivePlayAPIResponse.class);
            if (livePlayAPIResponse.getErrNo() != 0) {
                log.error("开启推送任务失败，错误信息: {}", livePlayAPIResponse.getErrorMsg());
                return false;
            }
        } catch (IOException e) {
            log.error("开启推送任务异常,e: {}", e.getMessage(), e);
            return false;
        }
        return true;
    }

    private boolean startOrStopLiveDataTask(String appID, String roomID, String msgType, boolean start) {
        // example: 通过java OkHttp库发起http请求,开发者可使用其余http访问形式
        OkHttpClient client = new OkHttpClient();
        String body = new JSONObject()
                .fluentPut("roomid", roomID)
                .fluentPut("appid", appID)
                .fluentPut("msg_type", msgType)
                .toString();
        String url="http://webcast.bytedance.com/api/live_data/task/start";
        String notice="开启";
        if(start){
            url="http://webcast.bytedance.com/api/live_data/task/stop";
            notice="停止";
        }
        Request request = new Request.Builder()
                .url(url) // 内网专线访问小玩法openAPI,无需https协议
                .addHeader("Content-Type", "application/json") // 无需维护access_token
                .post(
                        okhttp3.RequestBody.create(
                                MediaType.get("application/json; charset=utf-8"),
                                body
                        )
                )
                .build();

        try {
            Response httpResponse = client.newCall(request).execute();
            if (httpResponse.code() != 200) {
                log.error("开启推送任务失败,http访问非200");
                return false;
            }
            LivePlayAPIResponse livePlayAPIResponse
                    = JSON.parseObject(httpResponse.body().string(), LivePlayAPIResponse.class);
            if (livePlayAPIResponse.getErrNo() != 0) {
                log.error("{}推送任务失败，错误信息: {}",notice, livePlayAPIResponse.getErrorMsg());
                return false;
            }
        } catch (IOException e) {
            log.error("{}推送任务异常,e: {}",notice, e.getMessage(), e);
            return false;
        }
        return true;
    }

    /**
     * 结束玩法
     */
    @PostMapping(path = "/finish_game")
    public JsonResponse finishGameExample(HttpServletRequest httpRequest) {
        // TODO: 玩法对局结束,开发者自行实现对局结束逻辑
        JsonResponse response = new JsonResponse();
        response.success("结束玩法成功");
        return response;
    }


    /**
     * 通过抖音云服务接受直播间数据，内网专线加速+免域名备案
     * 通过内网专线会自动携带X-Anchor-OpenID字段
     * ref: <a href="https://developer.open-douyin.com/docs/resource/zh-CN/developer/tools/cloud/develop-guide/danmu-callback">...</a>
     */
    @PostMapping(path = "/live_data_callback")
    public JsonResponse liveDataCallbackExample(/*
            @RequestHeader("X-Anchor-OpenID") String anchorOpenID,
            @RequestHeader("x-msg-type") String msgType,*/
            HttpServletRequest httpRequest,
            @RequestBody String body) throws UnsupportedEncodingException {
        String anchorOpenID = httpRequest.getHeader("X-Anchor-OpenID");
        String msgType=httpRequest.getHeader("x-msg-type");
        String roomId=httpRequest.getHeader("x-roomid");
        Enumeration<String> headerNames = httpRequest.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            log.info("header: {}: {}", name, httpRequest.getHeader(name));
        }
        List<LiveDataModel> liveDataModelList = JSON.parseArray(body, LiveDataModel.class);
        log.info("收到{}消息,数量: {}, 消息内容: {}", msgType, liveDataModelList.size(), UNicode2Str.convertUnicodeEscapeSequences(body));
        redisUtil.set(roomId+":"+anchorOpenID+":"+msgType, UNicode2Str.convertUnicodeEscapeSequences(body));
        pushDataToDyServer(roomId,msgType,anchorOpenID,body);



//        System.out.println("receive is ====>"+body);
//        System.out.println("UTF-8->GB2312 receive is ====>"+new String(body.getBytes(Charset.forName("UTF-8")),"GB2312"));
//        System.out.println("UTF-8->GBK receive is ====>"+new String(body.getBytes(Charset.forName("UTF-8")),"GBK"));
//        System.out.println("ISO-8859-1->GBK receive is ====>"+new String(body.getBytes(Charset.forName("ISO-8859-1")),"GB2312"));
//        System.out.println("ISO-8859-1->UTF-8 receive is ====>"+new String(body.getBytes(Charset.forName("ISO-8859-1")),"UTF-8"));
//        System.out.println("US_ASCII->UTF-8 receive is ====>"+new String(body.getBytes(StandardCharsets.US_ASCII),"UTF-8"));
//        System.out.println("Unicode->UTF-8 receive is ====>"+new String(body.getBytes(Charset.forName("Unicode")),"UTF-8"));
//        System.out.println("Unicode->UTF-8 receive is ====>"+new String(body.getBytes(),"UTF-8"));
//        log.info("收到2消息详情: {}", "显示中文就乱码");
        liveDataModelList.forEach(liveDataModel ->
                pushDataToClientByDouyinCloudWebsocket(anchorOpenID, liveDataModel.getMsgID(), msgType, body)
        );
        JsonResponse response = new JsonResponse();
        response.success("success");
        return response;
    }

    public void pushDataToDyServer(String roomId,String msgType,String anchorOpenID,String sendData){
        //??live_gift??,??: 1, ????:
        //live_gift [{"msg_id":"7526842813808710710","sec_openid":"7520869510808802358","sec_gift_id":"n1/Dg1905sj1FyoBlQBvmbaDZFBNaKuKZH6zxHkv8Lg5x2cRfrKUTb8gzMs=","gift_num":1,"gift_value":10,"avatar_url":"https://p3-developer-sign.bytemaimg.com/tos-cn-i-ke512zj2cu/9b0abdfc8b9a4cc28b99430c3efe62f1~tplv-noop.jpeg?rk3s=3839646d&x-expires=1753084450&x-signature=Q2APxPPmtBHSbQnDLnhjvbNm%2Bpc%3D","nickname":"??????1","timestamp":1752479680113,"sec_magic_gift_id":"","test":true}]
        //live_like [{\"msg_id\":\"7521574897521923135\",\"sec_openid\":\"7520869510808802358\",\"like_num\":10,\"avatar_url\":\"https://p3-developer-sign.bytemaimg.com/tos-cn-i-ke512zj2cu/9b0abdfc8b9a4cc28b99430c3efe62f1~tplv-noop.jpeg?rk3s=3839646d&x-expires=1751851776&x-signature=VNAfIpJhVZexNwJSITnYrcYTE9c%3D\",\"nickname\":\"智汇中天测试1\",\"timestamp\":1751253142621}]"
        //live_comment [{\"msg_id\":\"7521574474949563411\",\"sec_openid\":\"7520869510808802358\",\"content\":\"good高兴boy!\",\"avatar_url\":\"https://p3-developer-sign.bytemaimg.com/tos-cn-i-ke512zj2cu/9b0abdfc8b9a4cc28b99430c3efe62f1~tplv-noop.jpeg?rk3s=3839646d&x-expires=1751851776&x-signature=VNAfIpJhVZexNwJSITnYrcYTE9c%3D\",\"nickname\":\"智汇中天测试1\",\"timestamp\":1751253068025}]"
        JSONArray objects = JSON.parseArray(sendData);
        OrderInfoDTO order = new OrderInfoDTO();
        order.setRoomId(roomId);

        for (int i = 0; i < objects.size(); i++) {
            JSONObject jsonObject = objects.getJSONObject(i);
            String msgID = jsonObject.getString("msg_id");
            String openId = jsonObject.getString("sec_openid");
            String giftId = jsonObject.getString("sec_gift_id");
            String nickName =  jsonObject.getString("nickname");
            String avatarUrl = jsonObject.getString("avatar_url");
            order.setOrderId(msgID);
            order.setGiftCode(giftId);
            order.setNickName(nickName);
            order.setAvatarUrl(avatarUrl);
            order.setOpenId(openId);
            //直播粉丝club, 直播点赞, 直播评论, 直播礼物
            if (msgType.equals("live_gift")){
                String giftNum = jsonObject.getString("gift_num");
                //String giftValue = jsonObject.getString("gift_value");
                order.setGiftNum(Integer.parseInt(giftNum));
                order.setOrderType("直播礼物");
            }else if(msgType.equals("live_like")){
                String linkeNum = jsonObject.getString("like_num");
                order.setGiftNum(Integer.parseInt(linkeNum));
                order.setOrderType("直播点赞");
            }else if(msgType.equals("live_comment")){
                String content = jsonObject.getString("content");
                order.setCommand(content);
                order.setOrderType("直播评论");
            }
            String orderStr = JSON.toJSONString(order);
            okHttpCall("http://www.sharekq.cn:9600/order", orderStr);
        }
    }

    public void okHttpCall(String url, String body) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .addHeader("Content-Type", "application/json")
                .post(
                        okhttp3.RequestBody.create(
                                MediaType.parse("application/json; charset=utf-8"),
                                body
                        )
                )
                .build();

        try {
            Response httpResponse = client.newCall(request).execute();
            log.info("dyServer9010 http call done, response: {}", JSON.toJSONString(httpResponse));
        } catch (IOException e) {
            log.error("dyServer9010 http call exception, e: ", e);
        }
    }


    //---------------- 抖音云websocket相关demo ---------------------

    /**
     * 抖音云websocket监听的回调函数,客户端建连/上行发消息都会走到该HTTP回调函数中
     * ref: <a href="https://developer.open-douyin.com/docs/resource/zh-CN/developer/tools/cloud/develop-guide/websocket-guide/websocket#%E5%BB%BA%E8%BF%9E%E8%AF%B7%E6%B1%82">...</a>
     */
    @RequestMapping(path = "/websocket_callback", method = {RequestMethod.POST, RequestMethod.GET})
    public JsonResponse websocketCallback(HttpServletRequest request) {

        String eventType = request.getHeader("x-tt-event-type");
        switch (eventType) {
            case "connect":
                // 客户端建连
            case "disconnect": {
                // 客户端断连
            }
            case "uplink": {
                // 客户端上行发消息
            }
            default:
                break;
        }
        JsonResponse response = new JsonResponse();
        response.success("success");
        return response;
    }

    /**
     * 使用抖音云websocket网关,将数据推送到主播端
     * ref: <a href="https://developer.open-douyin.com/docs/resource/zh-CN/developer/tools/cloud/develop-guide/websocket-guide/websocket#%E4%B8%8B%E8%A1%8C%E6%B6%88%E6%81%AF%E6%8E%A8%E9%80%81">...</a>
     */
    private void pushDataToClientByDouyinCloudWebsocket(String anchorOpenId, String msgID, String msgType, String data) {
        // 这里通过HTTP POST请求将数据推送给抖音云网关,进而抖音云网关推送给主播端
        OkHttpClient client = new OkHttpClient();

        Map<String, String> bodyMap = new HashMap<>();
        bodyMap.put("msg_id", msgID);
        bodyMap.put("msg_type", msgType);
        bodyMap.put("data", data);
//        bodyMap.put("extra_data", "");
        String bodyStr = JSON.toJSONString(bodyMap);

        Request request = new Request.Builder()
                .url("http://ws-push.dyc.ivolces.com/ws/live_interaction/push_data")   //.url("http://ws-push.dycloud-api.service/ws/live_interaction/push_data")
                .addHeader("Content-Type", "application/json")
                .addHeader("X-TT-WS-OPENIDS", JSON.toJSONString(Arrays.asList(anchorOpenId)))
                .post(
                        okhttp3.RequestBody.create(
                                MediaType.parse("application/json; charset=utf-8"),
                                bodyStr
                        )
                )
                .build();

        try {
            Response httpResponse = client.newCall(request).execute();
            //System.out.println("websocket http call done, SysOut  response=======> "+JSON.toJSONString(httpResponse));
            log.info("websocket http call done, response: {}", JSON.toJSONString(httpResponse));
        } catch (IOException e) {
            log.error("websocket http call exception, e: ", e);
        }
    }


    @GetMapping("/get_ws_conn_id")
    public JsonResponse getWSConnId(String service_id, String env_id) {
        // 这里通过HTTP POST请求将数据推送给抖音云网关,进而抖音云网关推送给主播端
        OkHttpClient client = new OkHttpClient();

        Map<String, String> bodyMap = new HashMap<>();
        bodyMap.put("service_id", service_id);
        bodyMap.put("env_id", env_id);
        //bodyMap.put("data", data);
//        bodyMap.put("extra_data", "");
        String bodyStr = JSON.toJSONString(bodyMap);

        Request request = new Request.Builder()
                .url("http://ws-push.dyc.ivolces.com/ws/get_conn_id")   //.url("http://ws-push.dycloud-api.service/ws/live_interaction/push_data")
                .addHeader("Content-Type", "application/json")
                //.addHeader("X-TT-WS-OPENIDS", JSON.toJSONString(Arrays.asList(anchorOpenId)))
                .post(
                        okhttp3.RequestBody.create(
                                MediaType.parse("application/json; charset=utf-8"),
                                bodyStr
                        )
                )
                .build();
        JsonResponse response = new JsonResponse();
        try {
            Response httpResponse = client.newCall(request).execute();
            String data=httpResponse.body().string();
            response.success(data);
            //System.out.println("websocket http call done, SysOut  response=======> "+JSON.toJSONString(httpResponse));
            //return JSON.toJSONString(httpResponse);
            log.info("websocket http call done, response: {}", JSON.toJSONString(httpResponse));

        } catch (IOException e) {
            log.error("websocket http call exception, e: ", e);
            response.failure("error");
        }
        return response;
    }
}
